package net.pimpas.util.reflection;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class ReflectionUtil {

	@SuppressWarnings("unchecked")
	public static boolean isSubclass(Class c, Class sub) {
		try {
			c.asSubclass(sub);
		} catch(ClassCastException e) {
			return false;
		}
		return true;
	}

	@SuppressWarnings("unchecked")
	public static boolean checkAnnotationInClass(Class ann, Class c) {
		return c.isAnnotationPresent(ann);
	}

	public static BeanInfo instanceBeanInfo(Class<?> clazz) {
		try {
			return Introspector.getBeanInfo(clazz);
		} catch (IntrospectionException e) {
			return null;
		}
	}
	
	public static Class<?> getListGenericType(Field list) {
		if(isSubclass(list.getType(), List.class)) {
			String awx = list.getGenericType().toString();
			String type = awx.substring(awx.indexOf("<")+1, awx.indexOf(">"));
			
			try {
				return Class.forName(type);
			} catch (ClassNotFoundException e) {
				return null;
			}
		}
		
		return null;
	}

	public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String property) {
		if(clazz == null || property == null)
			throw new IllegalArgumentException("class and/or property parameters cannot be null!");
		
		BeanInfo info = instanceBeanInfo(clazz);
		if(info == null) return null;
		for(int j=0;j<info.getPropertyDescriptors().length;j++)
			if(info.getPropertyDescriptors()[j].getName().equals(property))
				return info.getPropertyDescriptors()[j];

		return null;
	}
	
	public static MethodDescriptor getMethodDescriptor(Class<?> clazz, String method) {
		BeanInfo info = instanceBeanInfo(clazz);
		if(info == null) return null;
		
		for(int i=0;i<info.getMethodDescriptors().length;i++)
			if(info.getMethodDescriptors()[i].getName().equals(method))
				return info.getMethodDescriptors()[i];
		
		return null;
	}

	public static void fillBean(Map<String, Object> values, Object bean) throws IntrospectionException,
	IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		if(values == null || bean == null)
			throw new IllegalArgumentException("values and/or bean parameters cannot be null!");

		Object actual = null;
		// get key set
		Set<String> keys = values.keySet();
		Iterator<String> keysIterator = keys.iterator();
		String key = null;
		PropertyDescriptor property = null;
		String[] properties = null;
		while(keysIterator.hasNext()) {
			key = keysIterator.next();
			properties = key.split("\\.");
			actual = bean;
			for(int i=0;i<properties.length-1;i++) {
				property = getPropertyDescriptor(actual.getClass(), properties[i]);
				if(property == null)
					throw new IntrospectionException("Property ["+key+"] not found in class ["+bean.getClass().getSimpleName()+"]");
				actual = property.getReadMethod().invoke(actual);
			}

			property = getPropertyDescriptor(actual.getClass(), properties[properties.length-1]);
			if(property == null)
				throw new IntrospectionException("Property ["+key+"] not found in class ["+bean.getClass().getSimpleName()+"]");
			// try write
			property.getWriteMethod().invoke(actual, values.get(key));
		}
	}
}